﻿using System;
using System.IO;

namespace Ndns.Packet
{
    public struct DnsHostNamePointer : IComparable, IComparable<DnsHostNamePointer>, IEquatable<DnsHostNamePointer>
    {
        private byte _byte1;
        private byte _byte2;
        
        public ushort Position => (ushort)((_byte1 << 8) | _byte2);

        public static implicit operator ushort(DnsHostNamePointer p)
        {
            return (ushort)((p._byte1 << 8) | p._byte2);
        }

        public static explicit operator DnsHostNamePointer(ushort p)
        {
            return Create(p);
        }

        public static DnsHostNamePointer Create(ushort position)
        {
            return new DnsHostNamePointer()
            {
                _byte1 = (byte)((position & 0x3f00) >> 8),
                _byte2 = (byte)(position & 0xff)
            };
        }
        public static DnsHostNamePointer Create(byte b1, byte b2)
        {
            return new DnsHostNamePointer()
            {
                _byte1 = (byte)(b1 & 0x3f),
                _byte2 = b2
            };
        }

        public void WriteTo(Stream s)
        {
            s.WriteByte((byte)(_byte1 | 0xc0));
            s.WriteByte(_byte2);
        }

        public static bool IsPointer(byte b)
        {
            return (b & 0xc0) > 0;
        }
        public static bool IsPointer(ushort b)
        {
            return (b & 0xc000) > 0;
        }


        public static DnsHostNamePointer MaxValue => Create(0x3fff);
        public static DnsHostNamePointer MinValue => Create(0);

        public override int GetHashCode()
        {
            return (_byte1 << 8) | _byte2;
        }

        public override bool Equals(object obj)
        {
            if (obj is ushort || obj is byte || obj is sbyte)
                return Equals((ushort)obj);
            if (obj is DnsHostNamePointer)
                return Equals((DnsHostNamePointer)obj);
            return false;
        }
        public bool Equals(DnsHostNamePointer other)
        {
            return _byte1 == other._byte1 && _byte2 == other._byte2;
        }
        public bool Equals(ushort position)
        {
            return _byte1 == ((position & 0x3f00) >> 8) && _byte2 == (position & 0xff);
        }

        public int CompareTo(DnsHostNamePointer other)
        {
            return ((_byte1 - other._byte1) << 8) + (_byte2 - other._byte2);
        }

        public int CompareTo(ushort other)
        {
            return ((_byte1 - ((other & 0x3f00) >> 8)) << 8) + (_byte2 - (other & 0xff));
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj is ushort || obj is byte || obj is sbyte)
                return CompareTo((ushort)obj);
            if (obj is DnsHostNamePointer)
                return CompareTo((DnsHostNamePointer)obj);
            throw new InvalidOperationException();
        }

        public static bool operator ==(DnsHostNamePointer left, DnsHostNamePointer right) => left.Equals(right);
        public static bool operator !=(DnsHostNamePointer left, DnsHostNamePointer right) => !left.Equals(right);
        public static bool operator <(DnsHostNamePointer left, DnsHostNamePointer right) => left.CompareTo(right) < 0;
        public static bool operator >(DnsHostNamePointer left, DnsHostNamePointer right) => left.CompareTo(right) > 0;
        public static bool operator <=(DnsHostNamePointer left, DnsHostNamePointer right) => left.CompareTo(right) <= 0;
        public static bool operator >=(DnsHostNamePointer left, DnsHostNamePointer right) => left.CompareTo(right) <= 0;
    }
}
